/*
 * BikeTripItem.java
 *
 * Created on September 10, 2007, 4:17 PM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.trip;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import org.fpdev.core.FPEngine;
import org.fpdev.core.basenet.BLink;
import org.fpdev.core.basenet.BNode;
import org.fpdev.core.basenet.Path;
import org.fpdev.util.FPUtil;

/**
 * The bike-specific implementation of NonMotorizedTripItem.
 * 
 * @author demory
 */
public class BikeTripItem extends NonMotorizedTripItem {

  /** Creates a new instance of BikeTripItem */
  public BikeTripItem() {
  }

  public int getType() {
    return TripItem.TRIP_BIKE;
  }

  public String getItemName() {
    return "bike";
  }

  public void newSubtrip(Path path, int start, int end) {
    subtrips_.add(new BikeSubtrip(path, start, end));
  }

  public BufferedImage getElevGraph(FPEngine engine) {
    return getElevGraph(engine, 200);
  }

  public BufferedImage getElevGraph(FPEngine engine, int height) {
    
    Path path = super.getPath(engine);
    double ppm = 500; // horizontal pixels per mile
    int marginWidth = 40; // the left-hand margin space where labels, etc. are printed
    double tripLenFtD = (double) path.lengthFt(false); // trip length in feet
    double widthD = ppm * (tripLenFtD / 5280.0);
    int width = (int) widthD + marginWidth; // the total img width in pixels

    // calculate range of elevation display
    int minElev = getMinElev() / 100;
    minElev *= 100;
    int maxElev = getMaxElev();
    maxElev = maxElev / 100 + (maxElev % 100 > 0 ? 1 : 0);
    maxElev *= 100;
    double elevRange = (double) (maxElev - minElev);

    // set up the image
    BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = (Graphics2D) img.getGraphics();
    RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    renderHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g2d.setRenderingHints(renderHints);

    // fill the background
    g2d.setColor(Color.WHITE);
    g2d.fillRect(0, 0, width, height);
    double pos = 0; // position in total trip, 0 to 1
    int lastX = marginWidth;

    // draw the left-hand labels display
    g2d.setColor(Color.GRAY);
    g2d.fillRect(0, 0, marginWidth, 13);
    g2d.setColor(Color.LIGHT_GRAY);
    g2d.fillRect(0, 13, marginWidth, 12);
    g2d.setColor(new Color(0, 127, 0));
    g2d.fillRect(0, 25, marginWidth, (height - 25));
    g2d.setColor(Color.WHITE);
    int numLabels = (maxElev - minElev) / 100 + 1;
    for (int l = 0; l < numLabels; l++) {
      double y = ((double) l) / ((double) numLabels - 1) * (height - 25);
      if (l == 0) {
        y += 3;
      } else if (l == numLabels - 1) {
        y -= 12;
      }
      //else y -= 8;
      g2d.drawString((minElev + 100 * l) + "'", 5, height - (int) y);

    }

    // begin main loop through subtrips
    Color lastColor = null;
    for(NonMotorizedSubtrip subtrip : subtripsCopy()) {
      int len = subtrip.getPath().lengthFt(false);
      double lenD = (double) len;
      pos += lenD / tripLenFtD;
      double x = pos * widthD;
      int ix = (int) x + marginWidth;
      double fpWidthD = (double) (ix - lastX);
      g2d.setColor(Color.GRAY);
      g2d.fillRect(lastX, 0, (ix - lastX), 25);
      g2d.fillRect(lastX - 4, 0, 3, 13);
      g2d.setFont(new Font("Dialog", Font.PLAIN, 9));
      g2d.setColor(Color.WHITE);
      g2d.drawString(subtrip.getName(), lastX + 3, 10);
      g2d.setColor(Color.LIGHT_GRAY);
      g2d.fillRect(lastX, 13, (ix - lastX), 12);
      g2d.fillRect(lastX - 4, 13, 3, 12);
      g2d.setColor(Color.BLACK);
      g2d.drawString(FPUtil.distanceStr(len), lastX + 3, 22);


      Iterator<BLink> pLinks = subtrip.getPath().getLinks();
      Iterator<BNode> pNodes = subtrip.getPath().getNodes();
      pNodes.next();
      double fPos = 0, lastFPos = 0; // position on current facility
      while (pLinks.hasNext()) { // iterate through segments
        BLink link = pLinks.next();
        BNode tNode = pNodes.next();
        //System.out.println("link: "+link.getDisplayText());
        double sLen = link.getLengthFeetDbl(); // segment length
        fPos += sLen / lenD;

        int[] elevations = link.getElevArray();
        if (elevations == null) {
          lastFPos = fPos;
          continue;
        }
        double linkPosFt = 0, lastLinkPosFt = 0;
        for (int eli = 0; eli < elevations.length - 1; eli++) {
          
          double fElev = 0, tElev = 0; // the from and to elvations for the current link sub-segment

          if (link.getTNode() == tNode) { // forward link
            fElev = elevations[eli];
            tElev = elevations[eli + 1];
            if (link.getLengthFeet() % 100 > 0 && eli == elevations.length - 2) {
              linkPosFt += (link.getLengthFeet() % 100);
            } else {
              linkPosFt += 100;
            }
          } else { // backward link
            fElev = elevations[elevations.length - eli - 1];
            tElev = elevations[elevations.length - eli - 2];
            if (link.getLengthFeet() % 100 > 0 && eli == 0) {
              linkPosFt += (link.getLengthFeet() % 100);
            } else {
              linkPosFt += 100;
            }
          }

          double fPosSpan = fPos - lastFPos;
          double lPos1 = lastLinkPosFt / link.getLengthFeetDbl();
          double lPos2 = linkPosFt / link.getLengthFeetDbl();
          //System.out.println(" lp1,2 "+lPos1+" "+lPos2);

          double x1d = (lastFPos + (lPos1 * fPosSpan)) * fpWidthD, x2d = (lastFPos + (lPos2 * fPosSpan)) * fpWidthD;
          int x1 = lastX + (int) x1d, x2 = lastX + (int) x2d;
          double fElevPx = ((fElev - minElev) / elevRange) * (height - 25); //175.0;
          double tElevPx = ((tElev - minElev) / elevRange) * (height - 25); //175.0;

          Polygon poly = new Polygon();
          poly.addPoint(x1, height);
          poly.addPoint(x1, height - (int) fElevPx);
          poly.addPoint(x2, height - (int) tElevPx);
          poly.addPoint(x2, height);
          
          // compute grade and corresponding fill color
          double grade = (tElev-fElev) / ((lPos2 - lPos1)*link.getLengthFeetDbl());
          Color thisColor = this.getColorFromGrade(grade);
          if(lastColor == null) lastColor = thisColor;
          GradientPaint gradient = new GradientPaint(x1, 0, lastColor, x2, 0, thisColor);
          //g2d.setColor(this.getColorFromGrade(grade));
          g2d.setPaint(gradient);
          g2d.fillPolygon(poly);

          g2d.setColor(Color.DARK_GRAY);
          g2d.setStroke(new BasicStroke(2));
          g2d.drawLine(x1, height - (int) fElevPx, x2, height - (int) tElevPx);

          lastColor = thisColor;
          lastLinkPosFt = linkPosFt;
        }

        double x1d = lastFPos * fpWidthD, x2d = fPos * fpWidthD;
        int x1 = lastX + (int) x1d, x2 = lastX + (int) x2d - 1;

        if (pLinks.hasNext() && engine.getBaseNet().getGraph().degreeOf(tNode) > 2) {
          g2d.setColor(Color.GRAY);
          float[] dash = {2, 2};
          g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dash, 0));
          g2d.drawLine(x2, 25, x2, height);
          if (x2 - x1 > 12) {
            String isectStr = tNode.getIsectString(subtrip.getName(), engine.getBaseNet());

            g2d.setColor(Color.WHITE);
            Graphics2D g2dc = (Graphics2D) g2d.create();
            g2dc.translate(x2 - 11, 28);
            g2dc.rotate(Math.PI / 2);
            g2dc.drawString(isectStr, 0, 0);
            g2dc.dispose();

            g2d.setColor(Color.DARK_GRAY);
            g2dc = (Graphics2D) g2d.create();
            g2dc.translate(x2 - 10, 27);
            g2dc.rotate(Math.PI / 2);
            g2dc.drawString(isectStr, 0, 0);
            g2dc.dispose();
          }
        }

        lastFPos = fPos;
      }

      // draw divider between facilities
      g2d.setColor(Color.BLACK);
      g2d.fillRect(ix, 0, 1, height);
      lastX = ix + 1;
    }
    return img;
  }

  private Color getColorFromGrade(double grade) {
    double greenPct = grade > 0 ? grade/0.2 : 0;
    double redPct = grade < 0 ? -grade/0.2 : 0;

    if(greenPct > 1) greenPct = 1;
    if(redPct > 1) redPct = 1;

    double red = 255-redPct*255, green = 255-greenPct*255;
    return new Color((int) red, (int) green, 0);

  }
}
